﻿using pdfer.FileObjects;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace pdfer.Syntax
{
    internal class PSCompilationEngineV2
    {
        // 由于第一版本的实现有局限性所以开发第二个版本的
        // pdf内容的语法的描述
        // 基本的类型
        // Number Int Boolean Name Null String 引用类型
        // 组合类型
        // 字典 数组 流 
        // 注释
        // xref表
        // 语法描述

        // 关键词 obj endobj stream endstream startxref xref trailer
        // 分割字符 () <> [] {} / ％ <<>>
        // 空白字符 0 9 10 12 13 32 在不是流的上下文需要忽略
        // 

        // 注释 %开头 换行符结尾
        // 正文
        // 对象 编号 世代号 obj关键词 对象正文 数组或者字典 后面可选的流(stream endstream)

        // 读取的结果
        // 对象 数组

        //// No   类型 push pop
        //// 0    注释 %    \n|\r
        //// 1    对象 obj  endobject
        //// 2    字典 <<   >>
        //// 3    数组 [    ]
        //// 4    字符 (    )    在字符上下文中的时候成对的括弧忽略
        //private Stack<Int32> stack = new Stack<Int32>();

        public void ParseToPdfFileObject(Stream stream,Int64 offset)
        {
            InitContext(stream, offset);
            // 先读取Token
            GetTokens();
            // 在按token编译
            CreatePdfObject();
        }

        public PdfFileObject PdfFileObject { get { return pdfFileObject; } }

        private void CreatePdfObject()
        {
            // 创建的可能是数组
            // 字典 或者带id的字典 或者流
            // 也可能是Xref
            // 获取下第三个token是不是obj
            if(tokens.Count < 3)
            {
                // 可能是空的 数组或者字典
                // 也可能是单独的值
                return;
            }
            else if (tokens[0].Token == "xref")
            {
                // 交叉表
                InnerParseXref();
            }
            else
            {
                var tokenIdx = 0;
                if (tokens[2].Token == "obj")
                {
                    //对象
                    pdfFileObject.No = $"{tokens[0].Token} {tokens[1].Token}";
                    tokenIdx = 3;
                }
                else
                {
                    // 裸的字典或者数组 
                }
                if (tokens[tokenIdx].Token == "<<")
                {
                    tokenIdx++;
                    var dic = InnerParseDic(ref tokenIdx);
                    pdfFileObject.Dictionary = dic;
                }
                else if (tokens[tokenIdx].Token == "[")
                {
                    tokenIdx++;
                    var arr = InnerParseArr(ref tokenIdx);
                    pdfFileObject.List = arr;
                }
                else
                {
                    // 基本类型
                    pdfFileObject.Value = InnerParseBaseValue(ref tokenIdx);
                }
            }
        }

        private object InnerParseBaseValue(ref int i)
        {
            var _tmp = new List<PSTokenV2>();
            for(; i < tokens.Count; i++)
            {
                var __token = tokens[i];
                if(__token.Token == "endobj")
                {
                    break;
                }
                _tmp.Add(__token);
            }

            if(_tmp.Count > 0)
            {
                // 整数
                // 浮点
                // True False
                // null
                // 字符串
                // 名称
                var token = _tmp[0].Token;
                switch (_tmp[0].Type)
                {
                    case TokenType2.Keyword:
                        if(token.ToLower() == "null")
                        {
                            return null;
                        }
                        else if(token.ToLower() == "False")
                        {
                            return false;
                        }
                        else if (token.ToLower() == "true")
                        {
                            return false;
                        }
                        throw new Exception("语法错误");
                    case TokenType2.Number:
                        return Double.Parse(token);
                    case TokenType2.Int:
                        return Int32.Parse(token);
                    case TokenType2.String:
                    case TokenType2.HexString:
                    case TokenType2.Name:
                        return token;
                    default:
                        throw new Exception("语法错误");
                }

            }
            else
            {
                throw new Exception("语法错误");
            }
        }

        private void InnerParseXref()
        {
            // 交叉表

            // 文件尾字典


        }

        private Dictionary<String, Object> InnerParseDic(ref Int32 i)
        {
            var result = new Dictionary<String, Object>();
            String? key = null;
            List<Object> val = [];
            Boolean getKey = true;
            for (; i < tokens.Count; i++)
            {
                var __token = tokens[i];
                if (__token.Type == TokenType2.Name)
                {
                    if (getKey)
                    {
                        key = __token.Token;
                        getKey = false;
                    }
                    else
                    {
                        if (val.Count > 0)
                        {
                            result.Add(key, val[0]);
                            i--;
                            getKey = true;
                            key = null;
                            val = [];
                        }
                        else
                        {
                            val.Add(__token.Token);
                        }
                    }
                }
                else if (__token.Token == "[")
                {
                    i++;
                    result.Add(key, InnerParseArr(ref i));
                    getKey = true;
                    key = null;
                    val = [];
                }
                else if (__token.Token == "<<")
                {
                    i++;
                    result.Add(key, InnerParseDic(ref i));
                    getKey = true;
                    key = null;
                    val = [];
                }
                else if (__token.Token == ">>")
                {
                    if (!getKey)
                    {
                        result.Add(key, String.Join(' ', val));
                    }
                    if (tokens.Count <= (i + 1) || tokens[i+1].Token != "stream")
                    {
                        break;
                    }
                }
                else if (__token.Type == TokenType2.Keyword)
                {
                    if (__token.Token == "stream")
                    {
                        while (true)
                        {
                            i++;
                            __token = tokens[i];
                            if (__token.Token == "endstream")
                            {
                                break;
                            }
                            pdfFileObject.StreamData = __token.ByteToken;
                        }
                    }
                    else if (__token.Token == "endobj") 
                    {
                        break;
                    }
                    else if (__token.Token == "R")
                    {
                        val.Add(__token.Token);
                        // 间接引用
                        if (val.Count == 3)
                        {
                            result.Add(key, String.Join(' ', val));
                        }
                        else if (val.Count > 3)
                        {
                            // 这里应该直接报错 或者可能是数组
                            result.Add(key, new object[] { val.Slice(0, val.Count - 3), String.Join(' ', val[^3]) });
                        }
                        else
                        {
                            // 这里应该直接报错 < 3
                            result.Add(key, val);
                        }
                        getKey = true;
                        key = null;
                        val = [];
                    }
                }
                else
                {
                    val.Add(__token.Token);
                }
            }

            return result;
        }

        private List<Object> InnerParseArr(ref int i)
        {
            var result = new List<Object>();
            for (; i < tokens.Count; i++)
            {
                var __token = tokens[i];
                if (__token.Token == "<<")
                {
                    i++;
                    result.Add(InnerParseDic(ref i));
                }
                else if (__token.Token == "[")
                {
                    i++;
                    result.AddRange(InnerParseArr(ref i));
                }
                else if (__token.Token == "]" || __token.Token == "endobj")
                {
                    break;
                }
                else
                {
                    if (__token.Token == "R")
                    {
                        var v1 = result[^1];
                        var v2 = result[^2];
                        result.RemoveRange(result.Count - 2, 2);
                        result.Add($"{v2} {v1} {__token.Token}");
                    }
                    else
                    {
                        result.Add(__token.Token);
                    }
                }
            }
            return result;
        }

        private void GetTokens()
        {
            while (true)
            {
                var (wd, hasVal) = Next();
                if(!hasVal)
                {
                    break;
                }
                if (inComments)
                {
                    if(wd == 0x0D || wd == 0x0A)
                    {
                        inComments = false;
                    }
                    continue;
                }
                if (inStream)
                {
                    // 等待 endstream
                    token.Add(wd);
                    if (token.Count > 9 
                        && token[^9] == 0x65 // e
                        && token[^8] == 0x6E // n
                        && token[^7] == 0x64 // d
                        && token[^6] == 0x73 // s
                        && token[^5] == 0x74 // t
                        && token[^4] == 0x72 // r
                        && token[^3] == 0x65 // e
                        && token[^2] == 0x61 // a
                        && token[^1] == 0x6D // m
                                         ) 
                    {
                        var start = token[0] == 0x0A ? 1 : 0;
                        var end = 9;
                        while (token[^end] == 0x0A || token[^end] == 0x0D)
                        {
                            end++;
                        }
                        NewToken(_idx, [.. token[start..^end]], true);
                        NewToken(_idx, [.. token[^9..]], false);
                        inStream = false;
                        token = [];
                    }
                }
                else
                {
                    if (RegWD.IsMatch(((Char)wd).ToString()) 
                        || wd == '.' // 数字
                        || wd == '-' // 数字符号 命名里面带有
                        || wd == '_' 
                        || wd == '#' // CJK（中文、日文和韩文） 字体程序名称的多字节编码需要用#标识
                        || wd == '+' // 字体子集
                        )
                    {
                        token.Add(wd);
                    }
                    else if (wd == '/')
                    {
                        if (token.Count > 0)
                        {
                            NewToken(_idx, [.. token]);
                            if (End())
                            {
                                break;
                            }
                            token = [];
                        }
                        token.Add(wd);
                    }
                    else if (wd == '\\')
                    {
                        // 转义的情况
                        (wd, hasVal) = Next();
                        if (!hasVal)
                        {
                            break;
                        }
                        token.Add(wd);
                    }
                    else if (wd == '<')
                    {
                        if (token.Count > 0)
                        {
                            NewToken(_idx, [.. token]);
                            token = [];
                        }
                        (wd, hasVal) = Next();
                        if (!hasVal)
                        {
                            break;
                        }
                        if(wd == '<')
                        {
                            token.Add((Byte)'<');
                            token.Add((Byte)'<');
                            NewToken(_idx, [.. token]);
                            if (End())
                            {
                                break;
                            }
                            token = [];
                        }
                        else
                        {
                            token.Add((Byte)'<');
                            token.Add(wd);
                            while (true) 
                            {
                                // HexString
                                (wd, hasVal) = Next();
                                if (!hasVal)
                                {
                                    // 字符串截断 报错
                                    throw new Exception("未结束的字符串");
                                }
                                token.Add(wd);
                                if (wd == '>')
                                {
                                    break;
                                }
                            }
                            NewToken(_idx, [.. token]);
                            if (End())
                            {
                                break;
                            }
                            token = [];
                        }
                    }
                    else if (wd == '>')
                    {
                        (wd, hasVal) = Next();
                        if(wd == '>')
                        {
                            if (token.Count > 0)
                            {
                                NewToken(_idx, token.ToArray());
                                token = new();
                            }
                            token.Add((Byte)'>');
                            token.Add((Byte)'>');
                            NewToken(_idx, [.. token]);
                            if (End())
                            {
                                break;
                            }
                            token = [];
                        }
                        else
                        {
                            throw new Exception("期待>> 实际 >");
                        }
                    }
                    else if (wd == '(')
                    {
                        if (token.Count > 0)
                        {
                            NewToken(_idx, token.ToArray());
                            token = [];
                        }
                        token.Add(wd);
                        var leftP = 1;
                        while (true)
                        {
                            (wd, hasVal) = Next();
                            if (!hasVal)
                            {
                                // 字符串截断 报错
                                throw new Exception("未结束的字符串");
                            }
                            token.Add(wd);
                            if (wd == '(')
                            {
                                if(token.Count < 3 || token[^2] != '\\')
                                {
                                    leftP++;
                                }
                            }
                            else if (wd == ')')
                            {
                                if (token.Count < 3 || token[^2] != '\\')
                                {
                                    leftP--;
                                }
                                if (leftP < 1)
                                {
                                    break;
                                }
                            }
                        }
                        NewToken(_idx, [.. token]);
                        if (End())
                        {
                            break;
                        }
                        token = [];
                    }
                    else if (wd == '\r' || wd == '\n' || wd == ' ')
                    {
                        if (token.Count > 0)
                        {
                            NewToken(_idx, [.. token]);
                            if (End())
                            {
                                break;
                            }
                            token = [];
                        }
                    }
                    else if (wd == '[' || wd == ']')
                    {
                        if (token.Count > 0)
                        {
                            NewToken(_idx, [.. token]);
                            if (End())
                            {
                                break;
                            }
                            token = [];
                        }
                        token.Add(wd);
                        NewToken(_idx, [.. token]);
                        if (End())
                        {
                            break;
                        }
                        token = [];
                    }
                    else if (wd == '%')
                    {
                        if (token.Count > 0)
                        {
                            NewToken(_idx, [.. token]);
                            if (End())
                            {
                                break;
                            }
                            token = [];
                        }
                        inComments = true;
                    }
                }
            }
        }

        private Boolean End()
        {
            var lastToken = tokens[^1];
            switch (lastToken.Token)
            {
                case "<<":
                    leftDic++;
                    return false;
                case "[":
                    leftArr++;
                    return false;
                case ">>":
                    leftDic--;
                    if(leftDic < 1)
                    {
                        return tokens[0].Token == "<<";
                    }
                    return false;
                case "]":
                    leftArr--;
                    if (leftArr < 1)
                    {
                        return tokens[0].Token == "[";
                    }
                    return false;
                case "endobj":
                    return true;
                case "startxref":
                    return true;
                default:
                    return false;

            }
            // 第三个是 obj 结尾是endobj
            // 第一个是 << 最后一个是 >> 关闭需要成对
            // 第一个是 [ 最后一个是 ]   关闭需要成对
        }

        private void NewToken(Int32 index, Byte[] list, Boolean isByte = false)
        {
            var result = new PSTokenV2 
            {
                Index = index - list.Length,
                Length = list.Length,
            };
            if (isByte)
            {
                result.ByteToken = list;
                result.Type = TokenType2.BinData;
            }
            else if (list[0] == '(' && list[^1] == ')')
            {
                result.Type = TokenType2.String;
                if (list.Length > 4 && (list[1] + list[2]) == 0xFE + 0xFF)
                {
                    var codingCode = list[1] == 0xFE ? 1201 : 1200;
                    var by = list.AsSpan().Slice(1, list.Length - 2);
                    if (by.IndexOf((Byte)'\\') > -1)
                    {
                        String ss = ParsePdfEncodingString(by, codingCode);
                        result.Token = ss;
                    }
                    else
                    {
                        var ss = Encoding.GetEncoding(codingCode).GetString(by);
                        result.Token = $"({ss})";
                    }
                }
                else
                {
                    result.Token = Encoding.UTF8.GetString(list);
                }
            }
            else
            {
                result.Token = Encoding.UTF8.GetString(list);
                result.Type = GetTokenType(result.Token);
                if(result.Type == TokenType2.Keyword)
                {
                    if (result.Token == "stream")
                    {
                        inStream = true;
                    }
                    else if (result.Token == "endstream")
                    {
                        inStream = false;
                    }
                }
            }
            tokens.Add(result);
        }

        private static string ParsePdfEncodingString(ReadOnlySpan<Byte> by, Int32 codingCode)
        {
            var by2 = new Byte[by.Length];
            var curr = 0;
            for (var __idx = 0; __idx < by.Length; __idx++)
            {
                if (by[__idx] == (Byte)'\\')
                {
                    // 查看下一个
                    var next = by[__idx + 1];
                    __idx++;
                    switch (next)
                    {
                        case 98:  // 0x62 b \b
                            next = 8;
                            break;
                        case 102: // 0x66 f \f
                            next = 12;
                            break;
                        case 110: // 0x6e n \n
                            next = 10;
                            break;
                        case 114: // 0x72 r \r
                            next = 13;
                            break;
                        case 116: // 0x74 t \t
                            next = 9;
                            break;
                        case 40:
                        case 41:
                        case 92:
                            break;
                        case 10:
                            continue;
                        case 13:
                            next = by[__idx + 1];
                            if (next == 10)
                            {
                                //移动_idx
                                // 忽略 \r
                                __idx++;
                            }
                            continue;
                        default:
                            {
                                var c = 0;
                                var _ = 0;
                                do
                                {
                                    next = by[__idx];
                                    if (next < 48 || next > 55)
                                    {
                                        break;
                                    }
                                    __idx++;
                                    _ = (_ << 3) + next - 48;
                                    c++;
                                }
                                while (c < 3);
                                if (c > 2)
                                {
                                    next = (Byte)(_ & 0xff);
                                }
                                break;
                            }
                    }
                    by2[curr++] = next;
                }
                else
                {
                    by2[curr++] = by[__idx];
                }
            }
            var ss = Encoding.GetEncoding(codingCode).GetString(by2.AsSpan(0, curr));
            return $"({ss})";
        }

        private TokenType2 GetTokenType(string token)
        {
            if (String.IsNullOrEmpty(token))
            {
                return TokenType2.None;
            }
            if (symbolTable1.Any(p => p == token.ToLower()))
            {
                return TokenType2.Symbol;
            }
            if (keywordTable.Any(p => p == token.ToLower()))
            {
                return TokenType2.Keyword;
            }
            if (token.StartsWith('<') && token.EndsWith('>'))
            {
                return TokenType2.HexString;
            }
            if (token.StartsWith('/'))
            {
                return TokenType2.Name;
            }
            if (token.IndexOf('.') > -1)
            {
                if (Double.TryParse(token, out _))
                {
                    return TokenType2.Number;
                }
            }
            if (Int32.TryParse(token, out _))
            {
                return TokenType2.Int;
            }
            throw new NotSupportedException($"未知的Token类型： {token}");
        }

        #region context
        private Stream _stream;
        private Byte[] _buff;
        private Int32 _idx;
        private Int32 _ptr;
        private Int32 _buffDataLen;
        private Boolean inStream = false;
        private Boolean inComments = false;
        private List<PSTokenV2> tokens;
        private static Regex RegWD = new(@"\w");
        private static List<String> symbolTable1 = new() { "<<", "[", ">>", "]" };
        private static List<String> keywordTable = ["obj", "endobj", "stream", "endstream", "null", "true", "false", "r"];
        private List<Byte> token;
        private Int32 leftDic;
        private Int32 leftArr;

        private PdfFileObject  pdfFileObject;

        #endregion

        private void InitContext(Stream stream, Int64 offset)
        {
            tokens = [];
            token = [];
            _stream = stream;
            stream.Position = offset;
            _buff = new Byte[1024];
            _ptr = 1024;
            _idx = 0;
            _buffDataLen = 1024;
            inStream = false;
            inComments = false;
            leftDic = 0;
            leftArr = 0;
            pdfFileObject = new PdfFileObject();
        }

        private (Byte, Boolean) Next()
        {
            if (_ptr == _buffDataLen)
            {
                _buffDataLen = _stream.Read(_buff);
                _ptr = 0;
                if(_buffDataLen == 0)
                {
                    return (0, false);
                }
            }
            var wd = _buff[_ptr++];
            _idx++;
            return (wd, true);
        }

        // xref表 xref 也可能是一个字典或者标准的xref表


    }

    internal class PSTokenV2
    {
        /// <summary>
        /// token和起始位置的偏移量
        /// </summary>
        public Int32 Index { get; set; }
        /// <summary>
        /// Token的类型
        /// </summary>
        public TokenType2 Type { get; set; }
        /// <summary>
        /// Token的长度
        /// </summary>
        public Int32 Length { get; set; }
        /// <summary>
        /// Token的值
        /// </summary>
        public String Token { get; set; }
        /// <summary>
        /// 流里面的内容直接存放在此处
        /// </summary>
        public Byte[] ByteToken { get; set; }
    }

    internal enum TokenType2
    {
        /// <summary>
        /// 空的Token
        /// </summary>
        None = -1,
        /// <summary>
        /// obj endobj stream endstream null true false
        /// </summary>
        Keyword = 0,
        /// <summary>
        /// << >> []
        /// </summary>
        Symbol = 1,
        /// <summary>
        /// (xxxx)
        /// </summary>
        String = 2,
        /// <1D5E2F>
        HexString = 3, 
        /// 123 +123 -123
        Int = 4,  
        Number = 5,
        /// /xxxx
        Name = 6, 
        /// <summary>
        /// 流数据
        /// </summary>
        BinData = 7,
        /// <summary>
        /// 操作符
        /// </summary>
        Opera = 8

    }
}
